package org.wggds.webservices;

import java.awt.Frame;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eurocarbdb.MolecularFramework.io.CarbohydrateSequenceEncoding;
import org.eurocarbdb.application.glycanbuilder.BuilderWorkspace;
import org.eurocarbdb.application.glycanbuilder.Glycan;
import org.eurocarbdb.application.glycanbuilder.GlycanParser;
import org.eurocarbdb.application.glycanbuilder.GlycanParserFactory;
import org.eurocarbdb.application.glycanbuilder.GlycanRendererAWT;
import org.eurocarbdb.application.glycanbuilder.LogUtils;
import org.eurocarbdb.application.glycanbuilder.LoggerStorage;
import org.eurocarbdb.application.glycanbuilder.LoggerStorageIndex;
import org.eurocarbdb.application.glycanbuilder.LoggerStorageIndexImpl;
import org.eurocarbdb.application.glycanbuilder.MassOptions;
import org.eurocarbdb.application.glycoworkbench.plugin.StructureDictionary;
import org.eurocarbdb.application.glycoworkbench.plugin.StructureType;
import org.wggds.webservices.AbstractBasicHttpServletExample;
import org.wggds.webservices.io.WggdsResultException;
import org.wggds.webservices.io.WggdsResultUtilXml;
import org.wggds.webservices.io.data.CompleteInformation;
import org.wggds.webservices.io.data.QueryResult;
import org.wggds.webservices.io.query.CompositionSearchQuery;
import org.wggds.webservices.io.query.IdListQuery;
import org.wggds.webservices.io.query.ListOperationQuery;
import org.wggds.webservices.io.query.MassSearchQuery;
import org.wggds.webservices.io.query.SubstructureQuery;
import org.wggds.webservices.io.query.substructuresearch.SubStructureSearch;
import org.wggds.webservices.io.query.substructuresearch.SubStructureSearch.SearchMode;
import org.wggds.webservices.io.query.substructuresearch.SubStructureSearchImpl;


public class ExampleWggdsServer extends AbstractBasicHttpServletExample{
	public static HashMap<String,Glycan> glycanSeqCache=new HashMap<String,Glycan>();
	
	public ExampleWggdsServer(){
		initialiseStaticResources();
		try{
			initialiseGlycanCache();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	public String getGlydeVersion(){
		return "2";
	}

	@Override
	public String getNamespace(){
		return "igg";
	}

	@Override
	public List<QueryResult> runCompositionSearchQuery(CompositionSearchQuery query,
					HttpServletRequest req, HttpServletResponse resp, WggdsResultUtilXml xmlWriter){
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<QueryResult> runIdListQuery(IdListQuery query, HttpServletRequest req,
					HttpServletResponse resp, WggdsResultUtilXml xmlWriter){
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<QueryResult> runListOperationQuery(ListOperationQuery query,
					HttpServletRequest req, HttpServletResponse resp, WggdsResultUtilXml xmlWriter){
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<QueryResult> runMassSearchQuery(MassSearchQuery query, HttpServletRequest req,
					HttpServletResponse resp, WggdsResultUtilXml xmlWriter){
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<QueryResult> runSubStructureQuery(SubstructureQuery query, HttpServletRequest req,
					HttpServletResponse resp, WggdsResultUtilXml xmlWriter)
					throws WggdsResultException{
		List<QueryResult> results=new ArrayList<QueryResult>();
	
		System.err.println(query.getSequence()+"|"+query.isReducingEnd());
		
		try{
			GlycanParser glydeParser=GlycanParserFactory.getParser(CarbohydrateSequenceEncoding.glyde.getId());
			if(query.getSequence().equals("")){
				System.out.println("Sending all sequences");
				int i=0;
				int total=glycanSeqCache.size();
				for(String id:glycanSeqCache.keySet()){
					i++;
					
					if(i%1000==0){
						System.out.println(i+"/"+total);
					}
					
					Glycan other=glycanSeqCache.get(id);
					QueryResult result=new QueryResult();
					if(query.isCompleteInformation()!=CompleteInformation.None){
						result.setId(id);
					}

					if(query.isCompleteInformation()==CompleteInformation.Complete){
						result.setSequence(glydeParser.writeGlycan(other));
					}

					results.add(result);
				}
				
				System.out.println("Sending done");
				
				return results;
			}else{
				Glycan glycan=glydeParser.readGlycan(query.getSequence(), new MassOptions());
				
				SubStructureSearch subStructureSearch=new SubStructureSearchImpl();
				
				subStructureSearch.setQuery(glycan);
				
				subStructureSearch.setCompleteLeafMatch(query.isTerminal());
				subStructureSearch.setRedEndMatch(query.isReducingEnd());
				
				if(query.isOtherResidues()==false){
					subStructureSearch.setCompleteLeafMatch(false);
					subStructureSearch.setRedEndMatch(false);
				}
				
				if(query.isExactMatch()){
					subStructureSearch.searchMode(SearchMode.EQUIVALENT);
				}else{
					subStructureSearch.searchMode(SearchMode.FUZZY);
				}
					
				for(String id:glycanSeqCache.keySet()){
					Glycan other=glycanSeqCache.get(id);
					
					if(other!=null && subStructureSearch.match(other)){
						//System.out.println("Processing");
						try{
							QueryResult result=new QueryResult();

							if(query.isCompleteInformation()!=CompleteInformation.None){
								result.setId(id);
							}

							if(query.isCompleteInformation()==CompleteInformation.Complete){
								result.setSequence(glydeParser.writeGlycan(other));
							}

							results.add(result);
						}catch(Exception ex){
							//TODO: something graceful
							//ex.printStackTrace();
						}
					}
				}
				return results;
			}
		}catch(Exception ex){
			ex.printStackTrace();
		}
		
		return null;
	}
	
	public static void initialiseStaticResources(){
		/**
		 * There are some static dictionary type resources that must be initialised for glycan parsing etc..
		 * 
		 * BuilderWorkspace takes care to initialise these resources if it's own static field "loaded" is false
		 * during initialisation.
		 * 
		 * Not all uses of these static resources are bounded by BuilderWorkspace instances, so we create a new
		 * instance here so that they are loaded.
		 * 
		 * BuilderWorkspace and all other GlycanBuilder and GlycoWorkbench code wasn't meant to be run within the
		 * context of a web server.  There are therefore potential issues anywhere a static field is not final and
		 * initialised on declaration - loaded is one such example (it needs synchronised access).
		 */
		@SuppressWarnings("unused")
		BuilderWorkspace workspace=new BuilderWorkspace(new GlycanRendererAWT());
	}
	
	public static void initialiseGlycanCache() throws Exception{
		System.out.println("Starting caching jobs");
		
		final MassOptions options=new MassOptions();
		
		StructureDictionary dict=new StructureDictionary("/conf/carbbankraw_dict.gwd",false, null);

		GlycanParser glydeParser=GlycanParserFactory.getParser(CarbohydrateSequenceEncoding.glyde.getId());

		LogUtils.setLookupLogger(new LoggerStorageIndex() {
			LoggerStorage logger=new LoggerStorage(){

				@Override
				public Frame getFrameOwner() {
					// TODO Auto-generated method stub
					return null;
				}

				@Override
				public boolean getGraphicalReport() {
					// TODO Auto-generated method stub
					return false;
				}

				@Override
				public boolean getHasLastError() {
					// TODO Auto-generated method stub
					return false;
				}

				@Override
				public String getLastError() {
					// TODO Auto-generated method stub
					return null;
				}

				@Override
				public String getLastStackError() {
					// TODO Auto-generated method stub
					return null;
				}

				@Override
				public void setFrameOwner(Frame arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void setGraphicalReport(boolean arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void setHasLastError(boolean arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void setLastError(String arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void setLastStackError(String arg0) {
					// TODO Auto-generated method stub
					
				}
			};
			
			@Override
			public LoggerStorage getLogger() {
				return logger;
			}
		});
		
		int i=0;
		int total=dict.size();
		for(StructureType type:dict.getStructureTypes()){			
			if(i%1000==0){
				System.out.println(i+"/"+total);
			}
			
			if(i==1000){
				break;
			}
			
			i++;
			
			try {
				Glycan other=type.generateStructure();
				String glydeOutput=glydeParser.writeGlycan(other);
				if(glydeOutput!=null && !glydeOutput.equals("")){
					glycanSeqCache.put(""+i, other);
				}
			} catch (Exception e) {
				
			}
		}
		
		LogUtils.setLookupLogger(new LoggerStorageIndexImpl());

		System.out.println("Caching done");
	}
}